﻿@using AntDesign.Core.JsInterop.Modules.Components
@using AntDesign.JsInterop
@using System.Globalization
@inherits AntDesignTestBase

@code {
    
    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.KeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public void Key_applies_input_to_value<T>(string key, T value, T defaultValue, T expectedValue, string format, bool showTime) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true);

        string expectedValueAsString = expectedValue.ToString(format, null);
        var cut = Render<AntDesign.DatePicker<T>>(
			@<DatePicker @bind-Value="@value" DefaultValue="@defaultValue" ShowTime="showTime" />
    );
        //Act
        var input = cut.Find("input");
        input.Input(expectedValueAsString);
        input.KeyDown(key);
        //Assert
        cut.Instance.Value.Should().Be(expectedValue);
        value.Should().Be(expectedValue);
        input.GetAttribute("value").Should().Be(expectedValueAsString);
    }

    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.InvalidKeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public void DefaultValue_applied_to_value_on_blur_when_input_not_valid<T>(T value, T defaultValue, string format, string keyedInput) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);

        string defaultValueAsString = defaultValue.ToString(format, null);
        var cut = Render<AntDesign.DatePicker<T>>(
			@<DatePicker @bind-Value="@value" DefaultValue="@defaultValue" />
    );
        //Act
        var input = cut.Find("input");
        input.Input(keyedInput);
        input.Blur();
        //Assert
        cut.Instance.Value.Should().Be(defaultValue);
        value.Should().Be(defaultValue);
        input.GetAttribute("value").Should().Be(defaultValueAsString);
    }

    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.ValidKeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public void Input_restored_to_previous_value_on_blur_if_not_confirmed_with_enter_key<T>(T value, T defaultValue, string format, string keyedInput) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);

        var defaultValueAsString = defaultValue.ToString(format, null);
        var cut = Render<AntDesign.DatePicker<T>>(
        @<DatePicker @bind-Value="@value" DefaultValue="@defaultValue" />
    );

        //Act
        var input = cut.Find("input");
        input.Input(keyedInput);
        input.Blur();
        //Assert
        cut.Instance.Value.Should().Be(defaultValue);
        value.Should().Be(defaultValue);
        input.GetAttribute("value").Should().Be(defaultValueAsString);
    }

    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.InvalidKeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public void Entering_wrong_date_reverts_to_default<T>(T value, T defaultValue, string format, string keyedInput) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);

        string defaultValueAsString = defaultValue.ToString(format, null);
        var cut = Render<AntDesign.DatePicker<T>>(
			@<DatePicker @bind-Value="@value" DefaultValue="@defaultValue" />
        );
        //Act
        var input = cut.Find("input");
        input.Input(keyedInput);
        input.KeyDown("ENTER");
        //Assert
        cut.Instance.Value.Should().Be(defaultValue);
        value.Should().Be(defaultValue);
        input.GetAttribute("value").Should().Be(defaultValueAsString);
    }

    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.InvalidKeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public void Entering_wrong_date_reverts_to_initial<T>(T value, T defaultValue, string format, string keyedInput) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);

        var cut = Render<AntDesign.DatePicker<T>>(@<DatePicker @bind-Value="@value" />);
        //Act
        var input = cut.Find("input");
        input.Input(keyedInput);
        input.KeyDown("ENTER");
        //Assert
        cut.Instance.Value.Should().Be(value);
        input.GetAttribute("value").Should().Be(value.ToString(format, null));
    }

    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.ValidKeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public async Task Entering_new_date_focuses_in_picker<T>(T value, T expectedValue, string format, string keyedInput) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.Setup<HtmlElement>(JSInteropConstants.GetDomInfo, _ => true)
            .SetResult(new HtmlElement() { AbsoluteTop = 1, AbsoluteLeft = 1 });
        JSInterop.Setup<OverlayPosition>(JSInteropConstants.OverlayComponentHelper.AddOverlayToContainer, _ => true)
            .SetResult(new OverlayPosition() { Top = 0, Left = 0, ZIndex = 5000, Placement = Placement.BottomLeft });
        JSInterop.SetupVoid(JSInteropConstants.AddElementTo, _ => true);

        string expectedValueAsString = expectedValue.ToString(format, null);
        var cut = Render<AntDesign.DatePicker<T>>(@<DatePicker @bind-Value="@value" />);
        //Act
        var input = cut.Find("input");
        input.Click();
        //Task.Delay will force to wait for overlay to render the picker panel
        await Task.Delay(1);
        input.Input(expectedValueAsString);
        //Assert

        var expected = AntDesign.Internal.InternalConvert.ToDateTime(expectedValue)!.Value;

        string expectedMonth = cut.Instance.CultureInfo.DateTimeFormat.GetAbbreviatedMonthName(expected.Month);
        cut.WaitForAssertion(() => cut.Find("button.ant-picker-month-btn").TextContent.Trim().Should().Be(expectedMonth));
        cut.Find("button.ant-picker-year-btn").TextContent.Trim().Should().Be(expected.Year.ToString());
        var selectedCell = cut.Find("td.ant-picker-cell-selected");
        selectedCell.GetAttribute("title").Should().Be(expectedValueAsString);
        selectedCell.Children[0].TextContent.Trim().Should().Be(expected.Day.ToString());
    }

    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.KeyedInputs), MemberType = typeof(DatePickerKeyboardTestData))]
    public void Key_does_not_apply_input_to_value_when_disabledDate_is_set<T>(string key, T value, T defaultValue, T maxValue, string format, bool showTime) where T : struct, IFormattable
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true);

        string Format(T input)
        {
            return input.ToString(showTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd", null);
        }

        string inputValue = Format(maxValue);

        var dateTimeMax = AntDesign.Internal.InternalConvert.ToDateTime(maxValue)!.Value;

        var cut = Render<AntDesign.DatePicker<T>>(
			@<DatePicker @bind-Value="@value" DisabledDate="date=>date<=dateTimeMax" DefaultValue="@defaultValue" ShowTime="showTime" />
        );
        //Act
        var input = cut.Find("input");
        input.Input(inputValue);
        input.KeyDown(key);
        //Assert
        cut.Instance.Value.Should().Be(defaultValue);
        value.Should().Be(defaultValue);
        input.GetAttribute("value").Should().Be(Format(defaultValue));
    }


    [Theory]
    [MemberData(nameof(DatePickerKeyboardTestData.DateTimeOffsetData), MemberType = typeof(DatePickerKeyboardTestData))]
    public void Preserves_Offset_After_Input(DateTimeOffset value, DateTimeOffset newValue, string inputFormat)
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true);

        //Act
        var cut = Render<AntDesign.DatePicker<DateTimeOffset>>(
			@<DatePicker TValue="DateTimeOffset" DefaultValue="@value" />);

        var input = cut.Find("input");
        input.Input(newValue.ToString(inputFormat));
        input.KeyDown("ENTER");

        //Assert
        cut.Instance.Value.Should().Be(newValue);
        newValue.Offset.Should().Be(value.Offset);
    }
}